<?php


namespace Reezy\AlipaySdk;


use GuzzleHttp\Client;
use function json_encode;

class AlipaySdk
{
    public static function create(string $appId, string $appSecret): self
    {
        return new self($appId, $appSecret);
    }

    public static function createWithConfig(array $config) {
        $sdk = new self($config['app_id'], $config['app_secret']);
        if (isset($config['app_cert_path'])) {
            $sdk->useCerts($config['app_cert_path'], $config['root_cert_path'], $config['alipay_cert_path']);
        } elseif (!empty($config['alipay_public_key'])) {
            $sdk->setAlipayPublicKey($config['alipay_public_key']);
        }
        return $sdk;
    }

    protected $baseUrl = 'https://openapi.alipay.com/gateway.do';
    protected $appId;
    protected $appSecret;

    protected $appCertSn;
    protected $alipayRootCertSn;
    protected $alipayPublicKey;

    /**
     * @var Client
     */
    protected $http;

    /**
     * @param string $appId
     * @param string $appSecret
     */
    private function __construct(string $appId, string $appSecret)
    {
        if (empty($appId) || empty($appSecret)) {
            throw new AlipaySdkException("参数设置不完整。");
        }
        $this->appId = $appId;
        $this->appSecret = $appSecret;
        $this->http = new Client([
            "connectTimeout" => 10000,
            "readTimeout" => 10000,
            "retry" => [
                "maxAttempts" => 0
            ]
        ]);
    }

    public function useCerts($appCertPath, $alipayRootCertPath, $alipayCertPath): self
    {
        if (empty($appCertPath) || empty($alipayRootCertPath) || empty($alipayCertPath)) {
            throw new AlipaySdkException("证书参数设置不完整。");
        }
        $this->appCertSn = CertUtil::getCertSN($appCertPath);
        $this->alipayRootCertSn = CertUtil::getRootCertSN($alipayRootCertPath);
        $this->alipayPublicKey = CertUtil::getPublicKey($alipayCertPath);
        return $this;
    }

    public function oauth(string $code): OAuthClient
    {
        $resp = $this->execute('alipay.system.oauth.token', [
            'grant_type' => 'authorization_code',
            'code' => $code,
        ]);

        if (empty($resp['access_token'])) {
            throw new AlipaySdkException("授权失败，access_token 不能为空");
        }
        return new OAuthClient($this, $resp['access_token'], $resp['user_id']);
    }

    public function execute($api, $params = [], $bizParams = [])
    {
        $_params = [
            'app_id' => $this->appId,
            'version' => '1.0',
            'charset' => 'UTF-8',
            'format' => 'json',
            'sign_type' => 'RSA2',
            'alipay_sdk' => 'alipay-easysdk-php-2.0.0',

            'timestamp' => date("Y-m-d H:i:s"),
            'method' => $api,

            'app_cert_sn' => $this->appCertSn,
            'alipay_root_cert_sn' => $this->alipayRootCertSn,
        ];
        $params = $_params + $params;
        if (!empty($bizParams)) {
            $params['biz_content'] = json_encode($bizParams);
        }
        $params['sign'] = SignUtil::sign($params, $this->appSecret);

        $response = $this->http->post($this->baseUrl, [
            'headers' => [
                "content-type" => "application/x-www-form-urlencoded;charset=utf-8"
            ],
            'query' => $params,
            'form_params' => $bizParams,

        ]);

        $result = $this->parse($response->getBody()->getContents(), $api);

        if ($result === null) {
            throw new AlipaySdkException("响应解析失败[$api]");
        }
        return $result;

    }

    private function parse($responseContent, $api)
    {
        $json = json_decode($responseContent, true);
        if (empty($json)) return null;
        if (isset($json['error_response'])) return $json['error_response'];

        $key = str_replace(".", "_", $api) . "_response";
        if (isset($json[$key])) {
            if (!empty($this->alipayPublicKey)) {
                $signContent = SignUtil::parseSignContent($responseContent, $key);
                if (!SignUtil::verify($signContent, $json['sign'], $this->alipayPublicKey)) {
                    throw new AlipaySdkException("验签失败，请检查支付宝公钥设置是否正确。");
                }
            }
            return $json[$key];
        }
        return $json;
    }

    /**
     * @return string|null
     */
    public function getAlipayPublicKey()
    {
        return $this->alipayPublicKey;
    }

    /**
     * @param mixed $alipayPublicKey
     * @return AlipaySdk
     */
    public function setAlipayPublicKey($alipayPublicKey)
    {
        $this->alipayPublicKey = $alipayPublicKey;
        return $this;
    }

    /**
     * 加签模式是否为公钥证书模式
     * @return bool
     */
    public function isCertMode():bool {
        return !empty($this->appCertSn);
    }



}